<?
require_once(LIBS_PATH.'Adodb5/adodb.inc.php');
require_once('IDatabase.php');
/**
 * Klasa Database - implementuje interfejs IDatabase
 * Wzorzec singleton
 * @param $conn - przechowuje polaczenie do bazy zdefiniowane w konfiguracji aplikacji
 * @param static $instanceObj - przechowuje instancje klasy Database
 */
class Database implements IDatabase {
	private /*DB_Conn*/ $conn;
	private static /*Database*/ $instanceObj;

	/**
	 * Konstruktor prywatny - przypisuje do zmiennej $conn polaczenie zdefiniowane w klasie AppHelper
	 *
	 */
	private function __construct (){
		//ustawienia domyslne
		$config = Zend_Registry::getInstance()->get('config');
		

		$GLOBALS['ADODB_CACHE_DIR'] = $config->database->params->cache_dir;
		//$db =& ADONewConnection('mssql');
		$dsn = "Driver={".$config->database->params->dbdriver."};Server=".$config->database->params->host.";Database=".$config->database->params->dbname.";";
        
		/*if (extension_loaded('mssql')) {
			//echo 1;
			
			$this->conn =& ADONewConnection('mssql');
			$this->conn->Execute($config->database->params->host, $config->database->params->username, $config->database->params->password, $config->database->params->dbname);
        } else {
        	$this->conn = &ADONewConnection($config->database->adapter);
			$this->conn->Connect($dsn, $config->database->params->username, $config->database->params->password);
		//}
		*/
		
		$this->conn = ADONewConnection($config->database->adapter);
		$this->conn->Connect($config->database->params->host, $config->database->params->username, $config->database->params->password, $config->database->params->dbname);
		//$this->conn->debug=1;
		$this->conn->SetFetchMode(ADODB_FETCH_ASSOC);
	
		
		//debug($this->conn);
		
		$this->conn->Execute("SET NAMES 'UTF-8'");
		/*
		$this->conn->Execute("
			SET ANSI_NULL_DFLT_ON ON;
			SET NOCOUNT ON; 
			SET DATEFIRST 1; 
			SET CONCAT_NULL_YIELDS_NULL ON; 
			SET DATEFORMAT ymd;
		");
		*/
		
	}

	/**
	 * Metoda instance - Wzorzec singleton
	 *
	 * @return Database $instanceObj
	 */
	public static function instance() {
		if (!isset(self::$instanceObj)) {
			self::$instanceObj = new Database();
		}
		return self::$instanceObj;
	}

	/**
	 * Metoda statyczna getCon - zwraca polaczenie bazy danych
	 *
	 * @return DB_conn $conn
	 */
	public static function getConn() {
		return self::instance()->conn;
	}

	/**
	 * Opis metody ujety w interfejsie IDatabase
	 *
	 * @param string $stmt
	 * @return array $result
	 */
	public function getRow($stmt){
		$result = $this->conn->getRow($stmt);
	
		$errorCode = $this->conn->ErrorNo();
		if (($result === false) || $errorCode)
			throw new DatabaseException('error');

		return $result;
	}

	/**
	 * Opis metody ujety w interfejsie IDatabase
	 *
	 * @param string $stmt
	 * @return array $result
	 */
	public function getAll($stmt) {
		$result = $this->conn->getAll($stmt);
		$errorCode = $this->conn->ErrorNo();
		if (($result === false) || $errorCode)
			throw new DatabaseException('error');
		return $result;
	}

	/**
	 * Metoda getAllCached - sluzy do cache`owania zapytan wykonywanych, jako parametr dostaje jednostke czasu i zapytanie SQL
	 *
	 * @param integer(seconds) $time
	 * @param string $stmt
	 * @return array $result
	 */
	public function getAllCached($time, $stmt) {
		$result = $this->conn->cacheGetAll($time, $stmt);
		$errorCode = $this->conn->ErrorNo();
		if (($result === false) || $errorCode)
			throw new DatabaseException('error');

		return $result;
	}

	/**
	 * Metoda getLimit - zwraca ADORecordset na podstawie przekazanego zapytania limitu wierszy i offsetu
	 *
	 * @param string $stmt
	 * @param integer $numRows
	 * @param integer $offset
	 * @return array $result
	 */
	public function getLimit($stmt, $numRows=-1, $offset=-1) {
		if (($rs = $this->conn->SelectLimit($stmt, $numRows, $offset)) === false)
			throw new Exception ($this->conn->ErrorMsg());
		$result = $rs->GetAll();

		return $result;
	}

	/**
	 * Opis metody ujety w interfejsie IDatabase
	 *
	 * @param string $stmt
	 * @return $result
	 */
	public function getOne($stmt) {
		$result = $this->conn->getOne($stmt);
		$errorCode = $this->conn->ErrorNo();
		if (($result === false) || $errorCode)
			throw new DatabaseException('error');

		return $result;
	}

	/**
	 * Opis metody ujety w interfejsie IDatabase
	 *
	 * @param string $stmt
	 * @return array $result
	 */
	public function getCol($stmt) {
		$result = $this->conn->getCol($stmt);
		$errorCode = $this->conn->ErrorNo();
		if (($result === false) || $errorCode)
			throw new DatabaseException('error');

		return $result;
	}

	/**
	 * Metoda update - na podstawie przekazanych danych wykonuje metoda AutoExecute
	 *
	 * @param string $table
	 * @param array $arFields
	 * @param string $where
	 * @return ADORecordset $result
	 */
	public function update($table, $arFields, $where) {
		$result = $this->conn->AutoExecute($table, $arFields, 'UPDATE', $where, false, false);
		$errorCode = $this->conn->ErrorNo();
		if (($result === false) || $errorCode)
			throw new DatabaseException('error');

		return $this->conn->Affected_Rows();
	}

	/**
	 * Metoda insert - na podstawie przekazanych danych wykonuje metoda AutoExecute
	 *
	 * @param string $table
	 * @param array $arFields
	 * @param string $where
	 * @return integer $id - zwraca identyfikator dodanego wiersza
	 */
	public function insert($table, $arFields) {
		$result = $this->conn->AutoExecute($table, $arFields, 'INSERT', false, true, false);
		$errorCode = $this->conn->ErrorNo();
		if (($result === false) || $errorCode)
			throw new DatabaseException('error');

		$id = $this->conn->Insert_ID();
		return $id;
	}

	/**
	 * Metoda delete wykonuje zadane zapytanie SQL wywolujac metode Execute
	 *
	 * @param string $stmt
	 */
	public function delete($stmt) {
		$this->conn->execute($stmt);
	}

	/**
	 * Metoda cacheExecute - wlaczona w modelu jesli ustawiona w konfiguracji zmienna DEV_CACHE_QUERIES na true
	 *
	 * @param integer $cacheTime -seconds
	 * @param string $stmt
	 * @param array $values
	 * @return ADORecordSet $result
	 */
	public function cacheExecute($cacheTime, $stmt, $values = array()) {
		$result = $this->conn->cacheExecute($cacheTime, $stmt, $values);

		$errorCode = $this->conn->ErrorNo(); //uwaga pobranie kodu bledu resetuje go
		if (($result === false) || $errorCode)
			throw new DatabaseException('error');

		return $result;
	}

	/**
	 * Opis metody ujety w interfejsie IDatabase
	 *
	 * @param string $stmt
	 * @param array $values
	 * @return ADORecordset $result
	 */
	public function execute($stmt, $values = array()) {
		$result = $this->conn->execute($stmt, $values);
		
		$errorCode = $this->conn->ErrorNo(); //uwaga pobranie kodu bledu resetuje go
		
		try {
			if (($result === false) || $errorCode){
				
			}
		} catch (DatabaseException $e){
			$e->getMessage();
		}
		
		return $result;
	}

	/**
	 * Opis metody ujety w interfejsie IDatabase
	 *
	 */
	public function startTrans () {
		$this->conn->StartTrans();
	}

	/**
	 * Opis metody ujety w interfejsie IDatabase
	 *
	 */
	public function completeTrans () {
		$this->conn->completeTrans();
	}

	public function rollbackTrans () {
		$this->conn->rollbackTrans();
	}

	/**
	 * Opis metody ujety w interfejsie IDatabase
	 *
	 * @param string $stmt
	 * @return string $stmt
	 */
	public function prepare($stmt){
		//debug($stmt);
		return $this->conn->prepare($stmt);
	}

	/**
	 * Metoda doStatement zwraca recordset na podstawie zapytania $stmt i opcjonalnych parametrow $values
	 *
	 * @param string $stmt
	 * @param array $values
	 * @return ADORecordset
	 */
	function doStatement($stmt, $values, $cacheTime = null){
		if (is_null($cacheTime)){
			$result = $this->conn->Execute($stmt, $values);
		} else {
			$result = $this->conn->cacheExecute($cacheTime, $stmt, $values);
		}
		//$this->cacheTime = $this->defaultCacheTime;
		
		return $result;
	}
	
	/**
	 * Opis metody ujety w interfejsie IDatabase
	 *
	 * @param string $table
	 * @return integer $id
	 */
	public function nextID($table){
		return $this->conn->nextID($table);
	}

	/**
	 * Opis metody ujety w interfejsie IDatabase
	 *
	 * @param string $stmt
	 * @return array $result
	 */
	public function qstr($strVal) {
		return $this->conn->Qstr($strVal, false);
	}

	/**
	 * MS SQL - adowskie
	 */

	/**
	 * Metoda prepareBoundProc - buduje zapytanie SQL na podstawie przekazanych parametrow
	 *
	 * @param string $procname
	 * @param array $inParams
	 * @param array $outParams
	 * @return string $stmt
	 */
	public function &prepareBoundProc($procname, array $inParams = array(), array &$outParams = array()) {
		$stmt = $this->conn->PrepareSP($procname);
		if (is_array($inParams)) {
			foreach ($inParams as &$param) {
				//mssql_bind($stmt, $param['name'], $param['value'], $type, $isOutput, $isNull, $maxLen);
				if (isset($param['type']))
					$this->conn->InParameter($stmt, $param['value'], $param['name'], $param['maxlen'], $param['type']);
				elseif (isset($param['maxlen']))
					$this->conn->InParameter($stmt, $param['value'], $param['name'], $param['maxlen']);
				else
					$this->conn->InParameter($stmt, $param['value'], $param['name']);
			}
		}

		if (is_array($outParams)) {
			foreach ($outParams as &$param) {
				if (isset($param['type']))
					$this->conn->OutParameter($stmt, $param['value'], $param['name'], $param['maxlen'], $param['type']);
				elseif (isset($param['maxlen']))
					$this->conn->OutParameter($stmt, $param['value'], $param['name'], $param['maxlen']);
				else
					$this->conn->OutParameter($stmt, $param['value'], $param['name']);
			}
		}

		return $stmt;
	}

	/**
	 * Metoda prepareProc - buduje zapytanie SQL na podstawie przekazanych parametrow
	 *
	 * @param string $procname
	 * @param array $inParams
	 * @param array $outParams
	 * @return string $stmt
	 */
	public function prepareProc($procname, $inParams = array(), &$outParams = array()) {
		$proc = "EXEC ".$procname;
		$params = "";
		$declare = "";

		if (is_array($inParams)) {
			foreach ($inParams as &$param) {
				$value = ($param['value'] !== null) ? $this->qstr($param['value']) : 'NULL';
				$params .= "@".$param['name']."=".$value.", ";
			}
		}

		if (is_array($outParams)) {
			foreach ($outParams as &$param) {
				$value = ($param['value'] !== null) ? $this->qstr($param['value']) : 'NULL';
				$declare .= "DECLARE @".$param['name']." ".$param['type']." ";
				$declare .= "SET @".$param['name']."=".$value." ";
				$params .= "@".$param['name']."=@".$param['name']." OUTPUT, ";
			}
		}
		if ($params)
			$params = substr($params, 0, -2);

		$query = $declare."; ".$proc." ".$params.";";

		return $query;
	}

	/**
	 * Metoda executeProc - buduje zapytanie SQL na podstawie przekazanych parametrow i uruchamia metoda execute
	 *
	 * @param string $procname
	 * @param array $inParams
	 * @param array $outParams
	 * @return ADORecordset $result lub array $result
	 */
	public function executeProc($procname, array $inParams = array(), &$outParams = array(), $fetch = false) {
		$query = $this->prepareProc($procname, $inParams, $outParams);
		$select = "";

		//debug($outParams);
		if ($outParams) {
			$select = "SELECT ";
			foreach ($outParams as $param)
				$select .= "@".$param['name']." as ".$param['name'].", ";
			$select = substr($select, 0, -2);

			$row = $this->getRow($query." ".$select);

			//wpisanie wartosci parametrow Output
			foreach ($outParams as &$param) {
				//debug($param);				
				if (!empty($row[$param['name']]))
					$param['value'] = $row[$param['name']];
			}
		}
		elseif ($fetch)
			return $this->getAll($query);
		else
			$this->execute($query);
	}

	public static function flushCache($dir){
		/*$targetPath = $_SERVER['DOCUMENT_ROOT'] . $dir . '/';
		$mydir = opendir($targetPath);
		while(false !== ($file = readdir($mydir))) {
			if($file != "." && $file != "..") {
				chmod($dir.$file, 0777);
				if(is_dir($dir.$file)) {
					chdir('.');
					//unlink($dir.$file.'/');
					rmdir($dir.$file) or DIE("couldn't delete $dir$file<br />");
				} else
					unlink($dir.$file) or DIE("couldn't delete $dir$file<br />");
			}
		}
		closedir($mydir);*/
		//return self::instance()->getConn()->CacheFlush($sql=false,$inputarr=false);
	}
}

/**
 * Klasa DatabaseException - pochodna klasy Exception do obslugi bledow w klasie Database
 *
 */
class DatabaseException extends Exception {
	public function __construct($message, $code = 0) {
		parent::__construct($message, $code);
	}
}

?>
